package com.doge.walknovel.business.search

import androidx.lifecycle.MutableLiveData
import androidx.paging.DataSource
import com.doge.walknovel.api.*
import com.doge.walknovel.api.model.NovelInfo
import com.doge.walknovel.core.base.LoadStatus
import com.doge.walknovel.core.base.adapters.*
import com.doge.walknovel.utils.isMainThread
import com.doge.walknovel.utils.runOnWork

class SearchRepository {
    val loading = MutableLiveData<LoadStatus>()
    private var state: ListState<NovelInfo> = ListState()
    private var dataSource: XPagedKeyDataSource? = null
    private lateinit var searchKey: String

    fun setSearchKey(value: String) {
        searchKey = value
    }

    fun searchClear() = runOnWork {
        searchKey = ""
        state = state.copy(page = PAGE_INITIAL, list = listOf(), data = mapOf())
        dataSource?.invalidate()
    }

    fun request(initial: Boolean) {
        if (!::searchKey.isInitialized) return
        require(isMainThread())
        if (initial) loading.value = LoadStatus.LOADING
        val nextPage = if (initial) PAGE_INITIAL else (state.page + 1)
        api
            .novelSearch(searchKey, nextPage)
            .map(ApiResultMapTransformer())
            .subscribeOnIO()
            .observeOnWork()
            .subscribe(object : ObserverAdapter<List<NovelInfo>>() {
                override fun onNext(t: List<NovelInfo>) {
                    reduceOnNext(initial, t)
                }

                override fun onError(e: Throwable) {
                    reduceOnError(initial)
                }
            })
    }

    private fun reduceOnNext(initial: Boolean, data: List<NovelInfo>) {
        state = state.reduceOnNext(initial, data) {
            it.novelId.toString()
        }
        dataSource?.invalidate()
        loading.postValue(LoadStatus.SUCCESS)
    }

    private fun reduceOnError(initial: Boolean) {
        state = state.reduceOnError(initial)
        dataSource?.invalidate()
        loading.postValue(LoadStatus.FAILURE)
    }

    fun dataSource(): DataSource.Factory<Int, Cell> =
        object : DataSource.Factory<Int, Cell>() {
            override fun create(): DataSource<Int, Cell> {
                return object : XPagedKeyDataSource() {
                    override fun totalCount(): Int {
                        return state.list.size
                    }

                    override fun loadRange(start: Int, count: Int): List<Cell?> {
                        return state.list.mapToCell(start, count) {
                            Cell(ItemType.Search, it, extra = state.data[it])
                        }
                    }
                }.also { dataSource = it }
            }
        }
}